package com.ymt.bpm.appboot;

import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Properties;


/**
 * Created by Johnny on 2016/12/25.
 */
@Configuration
@EnableConfigurationProperties(value={MybatisProperties.class, DruidDataSourceConfig.class})
@ConfigurationProperties(prefix = "mybatisplugin")
@EnableTransactionManagement//启用事务控制
//@EnableAutoConfiguration
public class MybatisConfiguration {

    /**
     * Logger
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private DruidDataSourceConfig druidDataSourceConfig;

    private DataSource dataSource;  //defined in spring-config.xml

    @Autowired
    private MybatisProperties properties;

    @Autowired
    private ResourceLoader resourceLoader = new DefaultResourceLoader();

    @Autowired(required = false)
    private DatabaseIdProvider databaseIdProvider;

    @Autowired(required = false)
    private Interceptor[] interceptors;

    public MybatisProperties getProperties() {
        return properties;
    }

    public void setProperties(MybatisProperties properties) {
        this.properties = properties;
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public DatabaseIdProvider getDatabaseIdProvider() {
        return databaseIdProvider;
    }

    public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
        this.databaseIdProvider = databaseIdProvider;
    }

    public Interceptor[] getInterceptors() {
        return interceptors;
    }

    public void setInterceptors(Interceptor[] interceptors) {
        this.interceptors = interceptors;
    }

    @PostConstruct
    public void checkConfigFileExists() {
        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
            Assert.state(resource.exists(), "Cannot find config location: " + resource
                    + " (please add config file or check your Mybatis " + "configuration)");
        }

        logger.info("checkConfigFileExists...");
        logger.info("Initializing pagehelper plugin...");
        //配置mybatis 分页插件
        PageHelper pageHelper=new PageHelper();
        Properties phProperties = new Properties();
        phProperties.setProperty("dialect", this.druidDataSourceConfig.getPlatform());
        phProperties.setProperty("params","pageNum=start;pageSize=limit;pageSizeZero=zero;reasonable=false;count=contsql");
        pageHelper.setProperties(phProperties);
        if (this.interceptors==null) {
            this.interceptors = new Interceptor[]{pageHelper};
        }

        //Datasource
        if (this.dataSource == null) {
            this.dataSource = dataSource();
        }
    }

    @Bean
    public DataSource dataSource() {
        logger.info("druidDataSourceConfig: " + druidDataSourceConfig);
        logger.info("Bean >>> DruidDataSource dataSource");
        // 加载配置文件属性
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(druidDataSourceConfig.getDriverClassName());
        ds.setUsername(druidDataSourceConfig.getUsername());
        ds.setPassword(druidDataSourceConfig.getPassword());
        ds.setUrl(druidDataSourceConfig.getUrl());
        ds.setMaxActive(druidDataSourceConfig.getMaxActive());
        ds.setValidationQuery(druidDataSourceConfig.getValidationQuery());
        ds.setTestOnBorrow(druidDataSourceConfig.isTestOnBorrow());
        ds.setTestOnReturn(druidDataSourceConfig.isTestOnReturn());
        ds.setTestWhileIdle(druidDataSourceConfig.isTestWhileIdle());
        ds.setTimeBetweenEvictionRunsMillis(druidDataSourceConfig.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(druidDataSourceConfig.getMinEictableIdleTimeMillis());
        ds.setPoolPreparedStatements(druidDataSourceConfig.isPoolPreparedStatements());
        ds.setMaxOpenPreparedStatements(druidDataSourceConfig.getMaxOpenPreparedStatements());
        try {
            ds.setFilters(druidDataSourceConfig.getFilters());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return ds;
    }

    /**
     * 数据库会话工厂bean
     * @return
     */
    @Bean
    @ConditionalOnBean(DataSource.class)
    public SqlSessionFactory sqlSessionFactoryBean() throws Exception {

        logger.info("Bean >>> SqlSessionFactory sqlSessionFactoryBean");

        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        factory.setConfiguration(properties.getConfiguration());
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        return factory.getObject();
    }

    /**
     * 数据库会话模板bean
     * @param sqlSessionFactory
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        logger.info("Bean >>> SqlSessionTemplate sqlSessionTemplate");
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
            return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    /**
     * 事务管理器配置
     * @return
     */
    @Bean
    @ConditionalOnBean(DataSource.class)
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        logger.info("Bean >>> PlatformTransactionManager annotationDrivenTransactionManager");
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 事务管理器配置
     * @return
     */
    @Bean
    @ConditionalOnBean(DataSource.class)
    public DataSourceTransactionManager transactionManager() {
        logger.info("Bean >>> DataSourceTransactionManager transactionManager");
        return new DataSourceTransactionManager(dataSource);
    }

}
